using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;

public class WebController : MonoBehaviour {
    public GameObject PlayerPrefab;
    public GameObject cam;
    PlayerCharacter _playerController;
    private string secretKey = "mySecretKey"; // Edit this value and make sure it's the same as the one stored on the server
    public string addScoreURL = "http://gabrielvalforte.hosteie.me/addscore.php?"; //be sure to add a ? to your url
    public string getPlayerInfoURL = "http://gabrielvalforte.hosteie.me/playerinfo.php?";
    enum State {
        NONE,
        MAIN_MENU,
        CREATE,
        CREATING,
        CREATE_FAIL,
        JOIN,
        MASTER_SERVER_FAIL,
        JOINING,
        JOINING_GAME_BY_NAME,
        JOIN_FAIL,
        JOIN_PASSWORD,
        JOIN_SUCCESS
    }
    State state_ = State.MAIN_MENU;
    const string DEFAULT_GAME_NAME = "Unnamed Game";
    const string DEFAULT_PLAYER_NAME = "Unknown Player";
    const string GAME_IDENTIFIER = "aRPG Element Project";
    const string GAME_DISPLAY_IDENTIFIER = "aRPG Element Project";
    const int DEFAULT_PORT = 53000;
    const int MAX_PLAYERS = 6;
    const int MAX_CONNECTIONS = MAX_PLAYERS - 1;
    const int MIN_TEXT_FIELD_WIDTH = 200;
    HostData last_tried_server_ = null;
    bool chat_shown_ = false;
    string chat_ = "";
    int first_state_ui_update = 0;
    Vector2 scroll_pos = new Vector2();

    string queued_join_game_name_ = "";
    string game_name_ = "???";
    string game_ip_ = "";
    string player_name_ = "";
    string display_err_ = "???";
    string password_ = "";

    void Start() {
        ConsoleScript.Log(GAME_DISPLAY_IDENTIFIER);
        DontDestroyOnLoad(this);
    }

    void Update() {
        NetEvent net_event = NetEventScript.Instance().GetEvent();
        while(net_event != null) {
            switch(net_event.type()) {
                case NetEvent.Type.SERVER_INITIALIZED:
                    NetEventServerInitialized();
                    break;
                case NetEvent.Type.CONNECTED_TO_SERVER:
                    NetEventConnectedToServer();
                    break;
                case NetEvent.Type.FAILED_TO_CONNECT:
                    NetEventFailedToConnect(net_event);
                    break;
                case NetEvent.Type.FAILED_TO_CONNECT_TO_MASTER_SERVER:
                    NetEventFailedToConnectToMasterServer(net_event);
                    break;
                case NetEvent.Type.MASTER_SERVER_EVENT:
                    NetEventMasterServerEvent(net_event);
                    break;
                case NetEvent.Type.PLAYER_CONNECTED:
                    ConsoleScript.Log("Player " + net_event.network_player() + " connected");
                    break;
                case NetEvent.Type.PLAYER_DISCONNECTED:
                    NetEventPlayerDisconnected(net_event);
                    break;
                case NetEvent.Type.DISCONNECTED_FROM_SERVER:
                    NetEventDisconnectedFromServer(net_event);
                    break;
            }
            net_event = NetEventScript.Instance().GetEvent();
        }
    }

    public IEnumerator CreatePlayer() {
        var g = (GameObject)Network.Instantiate(PlayerPrefab, new Vector3(75f, 5f, 50f), Quaternion.identity, 1);
        var c = (GameObject)Instantiate(cam, g.transform.position, Quaternion.identity);
        CameraController cc = (CameraController)c.GetComponent("CameraController");
        cc.SetTarget(g.transform);
        camera.enabled = false;
        string index = g.networkView.owner.ToString();

        string hash = MD5Test.Md5Sum(index + secretKey);
        string post_url = getPlayerInfoURL + "id=" + WWW.EscapeURL(index) + "&hash=" + hash;
        print(post_url);

        print("Loading Player Info");
        WWW get_pi = new WWW(post_url);
        yield return get_pi;
        if(get_pi.error != null) {
            print("There was an error getting the player info: " + get_pi.error + "\nUsing Local values.");
            string playerData = PlayerPrefs.GetString("Player");
            print(playerData);
            _playerController = (PlayerCharacter)g.GetComponent(typeof(PlayerCharacter));
            _playerController.Assign(playerData);
            _playerController.networkView.RPC("Assign", RPCMode.OthersBuffered, playerData);
        }
        else {
            print(get_pi.text);
            _playerController = (PlayerCharacter)g.GetComponent(typeof(PlayerCharacter));
            _playerController.Assign(get_pi.text);
            _playerController.networkView.RPC("Assign", RPCMode.OthersBuffered, get_pi.text);
        }
    }

    void NetEventDisconnectedFromServer(NetEvent net_event) {
        switch(net_event.network_disconnection()) {
            case NetworkDisconnection.Disconnected:
                ConsoleScript.Log("Cleanly disconnected from server");
                break;
            case NetworkDisconnection.LostConnection:
                ConsoleScript.Log("Connection to server was lost unexpectedly");
                break;
        }
        if(state_ == State.NONE || state_ == State.JOIN_SUCCESS || state_ == State.JOINING) {
            Application.LoadLevel(Application.loadedLevel);
        }
    }

    void NetEventPlayerDisconnected(NetEvent net_event) {
        NetworkPlayer player = net_event.network_player();
        ConsoleScript.Log("Player " + player + " disconnected");
        PlayerListScript.Instance().Remove(int.Parse(player.ToString()));
        Network.RemoveRPCs(player);
        Network.DestroyPlayerObjects(player);
    }

    void NetEventConnectedToServer() {
        //colocar rotinas para os clients
        if(state_ == State.JOINING) {
            SetState(State.JOIN_SUCCESS);
        }
        ConsoleScript.Log("Connected to server with ID: " + Network.player);
        TellServerPlayerName(player_name_);
        StartCoroutine(CreatePlayer());
    }

    void NetEventServerInitialized() {
        if(state_ == State.CREATING) {
            SetState(State.NONE);
        }
        ConsoleScript.Log("Server initialized");
        TellServerPlayerName(player_name_);
        StartCoroutine(CreatePlayer());
    }

    void NetEventFailedToConnect(NetEvent net_event) {
        if(state_ == State.JOINING) {
            if(net_event.error() == NetworkConnectionError.InvalidPassword) {
                SetState(State.JOIN_PASSWORD);
            }
            else {
                display_err_ = "" + net_event.error();
                SetState(State.JOIN_FAIL);
            }
        }
        ConsoleScript.Log("Failed to connect: " + net_event.error());
    }

    void NetEventFailedToConnectToMasterServer(NetEvent net_event) {
        if(state_ == State.JOIN) {
            display_err_ = "" + net_event.error();
            SetState(State.MASTER_SERVER_FAIL);
        }
        ConsoleScript.Log("Failed to connect to master server: " + net_event.error());
    }

    void JoinHostListGameByName(string val) {
        HostData[] servers = MasterServer.PollHostList();
        foreach(HostData server in servers) {
            if(val == server.gameName) {
                ConnectToServer(server, "");
                return;
            }
        }
        display_err_ = "No game named \"" + val + "\" in host list";
        SetState(State.JOIN_FAIL);
    }

    void NetEventMasterServerEvent(NetEvent net_event) {
        switch(net_event.master_server_event()) {
            case MasterServerEvent.HostListReceived:
                ConsoleScript.Log("Received a host list from the master server.");
                if(queued_join_game_name_.Length > 0) {
                    JoinHostListGameByName(queued_join_game_name_);
                    queued_join_game_name_ = "";
                }
                break;
            case MasterServerEvent.RegistrationFailedGameName:
                ConsoleScript.Log("Registration failed because an empty game name was given.");
                break;
            case MasterServerEvent.RegistrationFailedGameType:
                ConsoleScript.Log("Registration failed because an empty game type was given.");
                break;
            case MasterServerEvent.RegistrationFailedNoServer:
                ConsoleScript.Log("Registration failed because no server is running.");
                break;
            case MasterServerEvent.RegistrationSucceeded:
                ConsoleScript.Log("Registration to master server succeeded, received confirmation.");
                break;
        }
    }

    void OnGUI() {
        switch(state_) {
            case State.NONE:
                DrawGameGUI();
                break;
            case State.MAIN_MENU:
                DrawMainMenuGUI();
                break;
            case State.CREATE:
                DrawCreateGUI();
                break;
            case State.CREATING:
                DrawCreatingGUI();
                break;
            case State.CREATE_FAIL:
                DrawCreateFailGUI();
                break;
            case State.JOIN:
                DrawJoinGUI();
                break;
            case State.JOINING:
                DrawJoiningGUI();
                break;
            case State.JOIN_FAIL:
                DrawJoinFailGUI();
                break;
            case State.JOIN_PASSWORD:
                DrawJoinPasswordGUI();
                break;
            case State.JOIN_SUCCESS:
                DrawJoinSuccessGUI();
                break;
            case State.MASTER_SERVER_FAIL:
                DrawMasterServerFailGUI();
                break;
        }
        ++first_state_ui_update;
    }

    void DrawMasterServerFailGUI() {
        GUILayout.BeginHorizontal();
        GUILayout.Label("Falha ao conectar-se ao servidor mestre.");
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        GUILayout.Label("Erro: " + display_err_);
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Tentar")) {
            SetState(State.JOIN);
        }
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Voltar")) {
            SetState(State.MAIN_MENU);
        }
        GUILayout.EndHorizontal();
    }

    void DrawJoinSuccessGUI() {
        GUILayout.BeginHorizontal();
        GUI.SetNextControlName("ALabel");
        GUILayout.Label("Entrou com sucesso no jogo: " + game_name_);
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        GUI.SetNextControlName("NameField");
        GUILayout.Label("Nome do jogador: ");
        player_name_ = GUILayout.TextField(player_name_, GUILayout.MinWidth(MIN_TEXT_FIELD_WIDTH));
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Continuar")) {
            TellServerPlayerName(player_name_);
            SetState(State.NONE);
        }
        GUILayout.EndHorizontal();
        if(first_state_ui_update == 1) {
            GUI.FocusControl("ALabel");
            GUI.FocusControl("NameField");
        }
    }

    void DrawJoinPasswordGUI() {
        GUILayout.BeginHorizontal();
        GUI.SetNextControlName("RandomLabel");
        GUILayout.Label(game_name_ + " precisa de senha:");
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        GUI.SetNextControlName("PasswordField");
        password_ = GUILayout.TextField(password_, GUILayout.MinWidth(MIN_TEXT_FIELD_WIDTH));
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Tente novamente")) {
            ConnectToServer(last_tried_server_, password_);
        }
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Voltar")) {
            SetState(State.JOIN);
        }
        GUILayout.EndHorizontal();
        if(first_state_ui_update == 1) {
            GUI.FocusControl("RandomLabel");
            GUI.FocusControl("PasswordField");
        }
    }

    void DrawJoinFailGUI() {
        GUILayout.BeginHorizontal();
        GUILayout.Label("Falha ao entrar no jogo: " + game_name_);
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        GUILayout.Label("Erro: " + display_err_);
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Voltar")) {
            SetState(State.JOIN);
        }
        GUILayout.EndHorizontal();
    }

    void DrawJoiningGUI() {
        GUILayout.BeginHorizontal();
        GUILayout.Label("Entrando no jogo: " + game_name_);
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Cancelar")) {
            Network.Disconnect();
            SetState(State.JOIN);
        }
        GUILayout.EndHorizontal();
    }

    void DrawJoinGUI() {
        GUILayout.BeginHorizontal();
        GUILayout.Label("Servidores disponiveis:");
        GUILayout.EndHorizontal();
        HostData[] servers = MasterServer.PollHostList();
        scroll_pos = GUILayout.BeginScrollView(scroll_pos);
        foreach(HostData server in servers) {
            GUILayout.BeginHorizontal();
            string display_name = server.gameName + " " + server.connectedPlayers + "/" + server.playerLimit;
            if(GUILayout.Button(display_name)) {
                ConnectToServer(server, "");
            }
            GUILayout.EndHorizontal();
        }
        GUILayout.EndScrollView();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Voltar")) {
            SetState(State.MAIN_MENU);
        }
        GUILayout.EndHorizontal();
    }

    void DrawCreateFailGUI() {
        GUILayout.BeginHorizontal();
        GUILayout.Label("Criacao do jogo falhou.");
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        GUILayout.Label("Erro: " + display_err_);
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Voltar")) {
            SetState(State.CREATE);
        }
        GUILayout.EndHorizontal();
    }

    void DrawCreatingGUI() {
        GUILayout.BeginHorizontal();
        GUILayout.Label("Tentando criar o jogo: " + game_name_);
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Cancelar")) {
            Network.Disconnect();
            SetState(State.CREATE);
        }
        GUILayout.EndHorizontal();
    }

    void DrawCreateGUI() {
        GUILayout.BeginHorizontal();
        GUILayout.Label("Nome do jogo: ");
        GUI.SetNextControlName("GameNameField");
        game_name_ = GUILayout.TextField(game_name_, GUILayout.MinWidth(MIN_TEXT_FIELD_WIDTH));
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        GUILayout.Label("Senha: ");
        password_ = GUILayout.TextField(password_, GUILayout.MinWidth(MIN_TEXT_FIELD_WIDTH));
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        GUILayout.Label("Nome do Jogador: ");
        GUI.SetNextControlName("PlayerNameField");
        player_name_ = GUILayout.TextField(player_name_, GUILayout.MinWidth(MIN_TEXT_FIELD_WIDTH));
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Criar")) {
            TryToCreateGame(false);
        }
        if(GUILayout.Button("Voltar")) {
            SetState(State.MAIN_MENU);
        }
        GUILayout.EndHorizontal();
        if(first_state_ui_update == 1) {
            GUI.FocusControl("GameNameField");
            GUI.FocusControl("PlayerNameField");
            GUI.FocusControl("GameNameField");
        }
    }

    void DrawGameGUI() {
        if(chat_shown_) {
            if(Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return) {
                if(chat_.Length > 0) {
                    SendChatMessage(chat_);
                }
                chat_ = "";
                chat_shown_ = false;
                GUI.FocusControl("TheLabel");
                Event.current.Use();
            }
            if(Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) {
                chat_ = "";
                chat_shown_ = false;
                GUI.FocusControl("TheLabel");
                Event.current.Use();
            }
        }
        else {
            if(Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return) {
                chat_shown_ = true;
                //ConsoleScript.Log ("Showing chat");
                Event.current.Use();
            }
        }
        if(chat_shown_) {
            GUILayout.BeginArea(new Rect(Screen.width * 0.5f - 200, Screen.height - 30, 400, 200));
            GUILayout.BeginHorizontal();
            GUI.SetNextControlName("TheLabel");
            GUILayout.Label("Chat:");
            GUI.SetNextControlName("ChatField");
            chat_ = GUILayout.TextField(chat_, GUILayout.MinWidth(350));
            if(chat_.Length > 90) {
                chat_ = chat_.Substring(0, 90);
            }
            GUI.FocusControl("ChatField");
            GUILayout.EndHorizontal();
            GUILayout.EndArea();
        }
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Exit Game")) {
            Network.Disconnect();
            Application.LoadLevel(Application.loadedLevel);
        }
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        GUILayout.Label(game_name_);
        GUILayout.EndHorizontal();
        Dictionary<int, PlayerInfo> player_info_list = PlayerListScript.Instance().GetPlayerInfoList();
        foreach(var pair in player_info_list) {
            GUI.contentColor = pair.Value.color_;
            GUILayout.BeginHorizontal();
            GUILayout.Label(pair.Key + ": " + pair.Value.name_);
            GUILayout.EndHorizontal();
            GUI.contentColor = Color.white;
        }
    }

    void DrawMainMenuGUI() {
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Jogo Local")) {
            TryToCreateGame(true);
        }
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Criar Jogo")) {
            SetState(State.CREATE);
        }
        GUILayout.EndHorizontal();
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Jogar Online")) {
            SetState(State.JOIN);
        }
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        GUILayout.Label("IP: ");
        GUI.SetNextControlName("GameIPField");
        game_ip_ = GUILayout.TextField(game_ip_, GUILayout.MinWidth(MIN_TEXT_FIELD_WIDTH));
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Coneccao Direta")) {
            DirectConnectToServer(game_ip_, password_);
        }
        GUILayout.EndHorizontal();
    }

    void SetState(State state) {
        first_state_ui_update = 0;
        switch(state) {
            case State.JOIN:
                MasterServer.RequestHostList(GAME_IDENTIFIER);
                password_ = "";
                break;
            case State.JOIN_SUCCESS:
                player_name_ = DEFAULT_PLAYER_NAME;
                break;
            case State.CREATE:
                game_name_ = DEFAULT_GAME_NAME;
                player_name_ = DEFAULT_PLAYER_NAME;
                password_ = "";
                break;
            case State.NONE:
                chat_shown_ = false;
                break;
        }
        state_ = state;
        //ConsoleScript.Log("Set state: "+state);
    }

    void TryToCreateGame(bool local) {
        SetState(State.CREATING);
        NetworkConnectionError err = CreateGame(local);
        if(err != NetworkConnectionError.NoError) {
            display_err_ = "" + err;
            SetState(State.CREATE_FAIL);
        }
    }

    NetworkConnectionError CreateGame(bool local) {
        NetworkConnectionError err;
        if(local) {
            err = Network.InitializeServer(1, DEFAULT_PORT, false);
        }
        else {
            Network.InitializeSecurity();
            Network.incomingPassword = password_;
            err = Network.InitializeServer(MAX_CONNECTIONS, DEFAULT_PORT, true);
            if(err == NetworkConnectionError.NoError) {
                MasterServer.RegisterHost(GAME_IDENTIFIER, game_name_, "Comments could go here");
            }
        }
        return err;
    }

    void DirectConnectToServer(string ip, string password) {
        SetState(State.JOINING);
        NetworkConnectionError err = Network.Connect(ip, 25000, password);
        if(err != NetworkConnectionError.NoError) {
            display_err_ = "" + err;
            SetState(State.JOIN_FAIL);
        }
    }

    void ConnectToServer(HostData server, string password) {
        last_tried_server_ = server;
        game_name_ = server.gameName;
        SetState(State.JOINING);
        NetworkConnectionError err = Network.Connect(server, password);
        if(err != NetworkConnectionError.NoError) {
            display_err_ = "" + err;
            SetState(State.JOIN_FAIL);
        }
    }

    void TellServerPlayerName(string name) {
        int player_id = int.Parse(Network.player.ToString());
        ConsoleScript.Log("Telling server that player " + player_id + " is named: " + player_name_);
        networkView.RPC("SetPlayerName", RPCMode.AllBuffered, player_id, name);
    }

    void SendChatMessage(string msg) {
        networkView.RPC("ReceiveChatMessage", RPCMode.All, Net.GetMyID(), msg);
    }

    [RPC]
    void ReceiveChatMessage(int id, string msg) {
        ConsoleScript.Log(PlayerListScript.Instance().GetPlayerInfoList()[id].name_ + ": " + msg);
    }

    Dictionary<string, string> ParseURLQuery(string val) {
        Dictionary<string, string> element_dictionary = new Dictionary<string, string>();
        string[] question_mark = val.Split('?');
        if(question_mark.Length > 1) {
            string query = question_mark[1];
            for(int i = 2; i < question_mark.Length; ++i) {
                query += '?' + question_mark[i];
            }
            string[] elements = query.Split('&');
            ConsoleScript.Log("Query parts:");
            foreach(string element in elements) {
                string[] parts = element.Split('=');
                if(parts.Length > 1) {
                    ConsoleScript.Log(parts[0] + ": " + parts[1]);
                    element_dictionary[parts[0]] = parts[1];
                }
            }
        }
        return element_dictionary;
    }
}